<?php

namespace App\Services;

use App\Exceptions\ApiException;
use App\Exports\ProjectBookTitleExport29;
use App\Exports\ProjectBookTitleExport8;
use App\Models\BookTitle;
use App\Models\PartCompany;
use App\Models\Project;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;

/**
 * 案卷目录 服务层类
 * Class BookTitleService
 * @package App\Services
 */
class BookTitleService extends BaseService
{
    public $bookCatalogService;

    public function __construct(BookCatalogService $bookCatalogService)
    {
        parent::__construct();
        $this->bookCatalogService = $bookCatalogService;
    }

    /**
     * 添加案卷目录
     * $needDate //这个参数是前端提交了 "start_end_date":"202209-202311", 验证合法性之后返回的 start_date: 2022-09-10 ; end_date : 2022-10-22
     */
    public function addNewData($params)
    {
        $partCompanyId     = $params['part_company_id'];
        $projectClassifyId = $params['project_classify_id'];
        $bookTitleInstance = $this->getBookTitleInstance($partCompanyId, $projectClassifyId);
        $thisQuery         = $bookTitleInstance
            ->where('part_company_id', $params['part_company_id'])
            ->where('project_classify_id', $params['project_classify_id']);

        $count = to_int($params['count']); //要插入的案卷数量
        if (request()->filled('id')) { //视为插入一行，插入到被选行的上方，然后更新当前案卷下的卷内序号
            self::isAllowInsert($bookTitleInstance, $params['id'], $count); //判断是否允许插入这么多数据
            $bookTitle = $bookTitleInstance->where('id', $params['id'])->select('serial_no', 'archives_no')->first();
            $serialNo  = $bookTitle->serial_no;
            $content   = "插入{$count}条案卷目录";
        } else { //获取本案卷最大的序号再 + 1
            $bookTitle = $thisQuery->latest('serial_no')->select('serial_no', 'archives_no')->first();
            $serialNo  = $bookTitle ? $bookTitle->serial_no + 1 : 1;
            $content   = "添加{$count}条案卷目录";
        }
        if (empty($bookTitle)) { //一条数据也没有的情况下新增数据
            $mainArchivesNo = CommonService::getBaseArchivesNo($partCompanyId, $projectClassifyId);
            $mainArchivesNo = $mainArchivesNo . 'XXX';
        } else {
            $archivesPart   = CommonService::getArchivesPart($bookTitle->archives_no);
            $mainArchivesNo = $archivesPart['main'];
        }
        $batchData = [];
        for ($i = 0; $i < $count; $i++) {
            if (request()->filled('id')) { //插入数据，序号递减
                $newSerialNo = $serialNo - $i - 1;
            } else { //新增数据，数据递增
                $newSerialNo = $serialNo + $i;
            }
            $newArchivesNo                        = $mainArchivesNo . '-' . $newSerialNo;
            $batchData[$i]['company_id']          = company_id();
            $batchData[$i]['part_company_id']     = $params['part_company_id'];
            $batchData[$i]['project_classify_id'] = $params['project_classify_id'];
            $batchData[$i]['serial_no']           = $newSerialNo;
            $batchData[$i]['archives_no']         = $newArchivesNo;
            $batchData[$i]['title']               = '这是案卷标题';
            $batchData[$i]['store_period']        = '永久';
            $batchData[$i]['creator_id']          = user_id();
            $batchData[$i]['created_at']          = now();
            $batchData[$i]['updated_at']          = now();
        }
        $bookTitleInstance->insert($batchData);
        CommonService::addProjectActionLog($content, $bookTitleInstance->id, $params['part_company_id'], $params['project_classify_id']);
        CommonService::isNeedUpdatePaperCount($partCompanyId, 1); //告诉参建单位需要更新下统计

        return true;
    }

    /**
     * 更新一条数据
     * 这里使用 ORM更新方式，是因为这样可以进行时间监听，记录变化
     */
    public function updateData($id, $params)
    {
        $partCompanyId     = $params['part_company_id'];
        $projectClassifyId = $params['project_classify_id'];
        $bookTitleInstance = $this->getBookTitleInstance($partCompanyId, $projectClassifyId);

        $bookTitle  = $bookTitleInstance->find($id);
        $updateData = [ //构造一个更新数组
            'serial_no'     => isset($params['serial_no']) ? format_str($params['serial_no']) : $bookTitle->serial_no, //柜号to_int($params['serial_no']),
            'title'         => format_str($params['title']),
            'page_num'      => to_int($params['page_num']),
            'archives_no'   => format_archives_no($params['archives_no']),
            'cabin_no'      => isset($params['cabin_no']) ? format_str($params['cabin_no']) : $bookTitle->cabin_no, //柜号
            'store_period'  => format_str($params['store_period']),
            'base_map'      => isset($params['base_map']) ? to_int($params['base_map']) : $bookTitle->base_map, //底图
            'volume'        => isset($params['volume']) ? to_int($params['volume']) : $bookTitle->volume, //册图
            'blueprint'     => isset($params['blueprint']) ? to_int($params['blueprint']) : $bookTitle->blueprint,
            'word_material' => isset($params['word_material']) ? to_int($params['word_material']) : $bookTitle->word_material,
            'remark'        => isset($params['remark']) ? format_str($params['remark']) : $bookTitle->remark,
            'mixed_no'      => isset($params['mixed_no']) ? format_str($params['mixed_no']) : $bookTitle->mixed_no, //互见号
            'secret_level'  => format_str($params['secret_level']), //密级
            'is_need_check' => 1, //如果案卷进行了修改，要重新检查
        ];
        $content = "更新一条案卷";
        $bookTitleInstance->where('id', $id)->update($updateData);

        $info = BookTitleMergeService::isHaveMerge($projectClassifyId);
        if (!empty($info) && $info->merge_count > 0) { //如果有关联案卷，并且合并过，同步更新关联案卷
            $partCompanyIds = to_array($info->part_company_ids);
            $partCompanyIds = array_diff($partCompanyIds, $partCompanyId); //排除当前案卷，已更新过
            foreach ($partCompanyIds as $val) {
                $bookTitleInstance = $this->getBookTitleInstance($val, $projectClassifyId);
                $bookTitleInstance->where('serial_no', $bookTitle->serial_no)->update($updateData);
            }
            $content = $content . '并同步更新合并案卷';
        }
        CommonService::addProjectActionLog($content, $id, $partCompanyId, $projectClassifyId);

        return $bookTitle->save();
    }

    /**
     * 验证输入的案卷数量是否合法
     */
    public static function checkCount($count, $maxCount = 100)
    {
        if (empty($count)) {
            return false;
        } elseif (!is_numeric($count)) {
            return false;
        } elseif ($count < 1 || $count > $maxCount) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * 判断加载的的扫描件文件名是否有对饮的档号
     */
    public function checkPdfFileName($partCompanyId, $projectClassifyId, $originalName)
    {
        $pos = strrpos($originalName, '-'); //最后'-'出现的位置
        $archivesNo = substr($originalName, 0, $pos);  //档案号
        $bookTitleInstance = CommonService::getBookTitleInstance($partCompanyId, $projectClassifyId);
        $bookTitle = $bookTitleInstance->where('archives_no', $archivesNo)->select('id', 'project_classify_id')->first();
        if (empty($bookTitle)) { //如果按照档案号找不到，把档案号的'--'使用 '/' 替换，看是否能找到
            if (str_contains($archivesNo, '--')) $archivesNo = str_replace('--', '/', $archivesNo);
            $bookTitle = $bookTitleInstance->where('archives_no', $archivesNo)->select('id', 'project_classify_id')->first();
            if (empty($bookTitle)) throw new ApiException("找不到案卷{$archivesNo}，档案号如果存在斜杠，斜杠使用双横杠--代替");
        }

        return $bookTitle->id;
    }

    /**
     * 限制用户1分钟内不得重复点击【检查案卷】
     */
    public function limitClickCount($projectClassifyId)
    {
        redis()->setex('click_count' . $projectClassifyId, 10, $projectClassifyId);
    }

    /**
     * 判断是否允许插入这么多数据
     */
    public static function isAllowInsert($bookTitleInstance, $id, $count)
    {
        $info = $bookTitleInstance->where('id', $id)->select('serial_no', 'archives_no')->first(); //选中的案卷

        $preSerialNo = $bookTitleInstance->where('serial_no', '<', $info->serial_no)->max('serial_no'); //上一条数据序号
        $nowSerialNo = $info->serial_no;
        $maxCount    = $nowSerialNo - $preSerialNo - 1; //最多可插入数量
        if ($maxCount == 0) {
            throw new ApiException('序号是连续的，没有插入新数据的位置');
        }
        if ($count > $maxCount) { //意思是如果两个序号之间最多容乃 5 条，比如 4和10之间，你要插入30条，肯定不合法的
            throw new ApiException("最多只能插入【{$maxCount}】条案卷");
        }
        return true;
    }

    /**
     * 获取扫描件已传完的案卷id
     *
     * @param $partCompanyId
     * @param $projectClassifyId
     * @return mixed
     * @throws ApiException
     * @author: lxk
     * @date: 2024-11-15 9:29
     */
    public function getPdfCompletedBtIds($partCompanyId, $projectClassifyId, $completed = 1)
    {
        $redisKey1           = get_rand_str('hadPdfBtIds');
        $redisKey2           = get_rand_str('noPdfBtIds');
        $bookTitleInstance   = $this->getBookTitleInstance($partCompanyId, $projectClassifyId);
        $bookCatalogInstance = $this->getBookCatalogInstance($partCompanyId, $projectClassifyId);
        $bookTitleInstance->select('id')->orderBy('id')
            ->chunkById(100, function ($list) use ($bookCatalogInstance, $redisKey1, $redisKey2) {
                foreach ($list as $val) {
                    $catalogs = $bookCatalogInstance->where('book_title_id', $val->id)->select('id', 'file_url')->get()->toArray();
                    $count1   = count(array_column($catalogs, 'id'));
                    $count2   = array_sum(array_column($catalogs, 'file_url'));
                    if ($count1 > 0 && $count1 == $count2) { //已完成
                        redis()->sadd($redisKey1, $val->id);
                    } else { //未完成
                        redis()->sadd($redisKey2, $val->id);
                    }
                }
            });
        $completedBtIds   = redis()->smembers($redisKey1);
        $noCompletedBtIds = redis()->smembers($redisKey2);

        redis()->del($redisKey1);
        redis()->del($redisKey2);

        return $completed ? $completedBtIds : $noCompletedBtIds;
    }

    /**
     * 获取 案卷目录 数据列表
     * Redis集合具有去重的特性，它不允许相同成员的存在。
     * 管理员和超管能看到合并后的全部数据
     * 普通用户仍然只看到自己那部分数据，合并过来的看不到
     */
    public function getList($partCompanyId, $projectClassifyId, $perPage, $keyword, $type, $searchType)
    {
        $bookTitleInstance = $this->getBookTitleInstance($partCompanyId, $projectClassifyId);
        $thisQuery = $bookTitleInstance->where('part_company_id', $partCompanyId);

        if ($type == 2) { //异常案卷
            $thisQuery->where('problem', '!=', '');
        } elseif ($type == 3) { //空案卷
            $thisQuery->where('is_have_data', '=', 0);
        } elseif ($type == 4) { //非空案卷
            $thisQuery->where('is_have_data', '=', 1);
        } elseif ($type == 5) { //扫描件已完成
            $bookTitlesIds = $this->getPdfCompletedBtIds($partCompanyId, $projectClassifyId);
            $thisQuery->whereIn('id', $bookTitlesIds);
        } elseif ($type == 6) { //扫描件未完成
            $bookTitlesIds = $this->getPdfCompletedBtIds($partCompanyId, $projectClassifyId, 0);
            $thisQuery->whereIn('id', $bookTitlesIds);
        }
        if ($keyword) {
            $likeKeyword = "%{$keyword}%";
            if ($searchType == 2) { //文件题名
                $thisQuery->whereIn('id', function (Builder $query) use ($partCompanyId, $projectClassifyId, $likeKeyword) {
                    $table = 'book_catalogs_' . $partCompanyId . '_' . $projectClassifyId;
                    $query->select('book_title_id')->from($table)->where("$table.title", 'like', $likeKeyword);
                });
            } elseif ($searchType == 3) { //档号检索
                $thisQuery->where('archives_no', 'like', $likeKeyword);
            } else { //案卷题名
                $thisQuery->where('title', 'like', $likeKeyword);
            }
        }
        $list = $thisQuery
            ->orderBy('serial_no', 'asc')
            ->orderBy('id', 'asc')
            ->select(['id', 'serial_no', 'part_company_id', 'project_classify_id', 'problem',
                'status', 'archives_no', 'title', 'base_map', 'blueprint', 'word_material', 'catalog_count', 'page_num',
                'store_period', 'is_have_data','cabin_no', 'volume', 'years', 'secret_level', 'start_date', 'end_date', 'remark', 'is_need_check'
            ])
            ->paginate($perPage); //按照序号从小到大

        if ($list->isEmpty()) return [];

        $list = $list->toArray();

        foreach ($list['data'] as &$val) { //格式化起止日期格式为 20220910~20220910
            $val['number']         = $val['serial_no'];
            $val['title']          = htmlspecialchars_decode($val['title']);
            $val['start_end_date'] = format_date($val['start_date'], $val['end_date']);
            $val['data_count']     = $val['catalog_count'];
            $val['operate']        = 1;
            if ($partCompanyId != $val['part_company_id']) { //如果不是本参建单位的，只能看，不能任何操作
                $val['operate'] = 0;
            }
            if ($val['data_count'] && !$val['is_have_data']) { //如果有卷内，但是is_have_data字段为0，更新为1
                $bookTitleInstance->where('id', $val['id'])->update(['is_have_data' => 1]);
            }
            if (empty($val['data_count'])) { //如果没有数据，更新下，看有没有新数据
                self::updateCatalogCount($partCompanyId, $projectClassifyId, $val['id']);
            }
            $pdfPageInfo            = $this->getPdfPageNum($partCompanyId, $projectClassifyId, $val['id']);
            $val['pdf_count']       = $pdfPageInfo['pdf_count'];
            $val['pdf_page_count']  = $pdfPageInfo['pdf_page_count'];
        }
        self::saveArchivesNoToRedis($bookTitleInstance, $projectClassifyId);

        //返回给前端，有时候能用到，比如批量上传PDF展示例子
        $archivesNo = $list['data'][0]['archives_no'].'-1';
        $list['archives_no_example'] = str_contains($archivesNo, '/') ? str_replace('/', '--', $archivesNo) : $archivesNo;

        //页面底部展示的统计
        $list['total_page_num']  = $thisQuery->sum('page_num');
        $list['total_blueprint'] = $thisQuery->sum('blueprint');

        return $this->removeNoUsedPageParams($list);
    }

    /**
     * 当卷内数据变化时
     * 更新案卷数据的catalog_count字段
     */
    public static function updateCatalogCount($partCompanyId, $projectClassifyId, $bookTitleId)
    {
        $ids = to_array($bookTitleId); //可以是个整数，也可以是个一维数组
        $bookTitleInstance   = CommonService::getBookTitleInstance($partCompanyId, $projectClassifyId);
        $bookCatalogInstance = CommonService::getBookCatalogInstance($partCompanyId, $projectClassifyId);
        foreach ($ids as $id) {
            $catalogCount = $bookCatalogInstance->where('book_title_id', $id)->count();
            $bookTitleInstance->where('id', $id)->update(['catalog_count' => $catalogCount, 'is_need_check' => 1]);
        }
    }

    /**
     * 获取一个案卷的扫描件页数、蓝图页数
     *
     * @param $partCompanyId
     * @param $projectClassifyId
     * @param $bookTitleId
     * @return array|int[]
     * @throws ApiException
     * @author: lxk
     * @date: 2024-10-30 15:11
     */
    public static function getPdfPageNum($partCompanyId, $projectClassifyId, $bookTitleId) : array
    {
        $list = CommonService::getBookCatalogInstance($partCompanyId, $projectClassifyId)
            ->where('book_title_id', $bookTitleId)
            ->select('id', 'serial_no', 'page_num', 'file_url', 'is_pic')
            ->get()
            ->toArray();

        $havePdfSerialNos       = []; //有扫描件的序号
        $haveBlueprintSerialNos = []; //有蓝图的序号
        $pageNum                = 0;  //卷内页数
        $pdfCount               = 0;  //扫描件数
        $pdfPageNum             = 0;  //扫描件张数
        $blueprintPageCount     = 0;  //蓝图页数

        foreach ($list as $val) {
            if ($val['file_url']) { //如果有PDF
                $pdfCount += 1;
                $havePdfSerialNos[] = $val['serial_no'];
            }
            if ($val['is_pic']) { //如果是蓝图
                $haveBlueprintSerialNos[] = $val['serial_no'];
            }
        }
        $serialNoPageNum = self::getSerialNoPageNum($list); //每个序号对应的页数
        foreach ($serialNoPageNum as $k=>$v) {
            $pageNum += $v;
            if (in_array($k, $havePdfSerialNos)) {
                $pdfPageNum += $v;
            }
            if (in_array($k, $haveBlueprintSerialNos)) {
                $blueprintPageCount += $v;
            }
        }
        return [
            'page_num'       => $pageNum,
            'pdf_count'      => $pdfCount,
            'pdf_page_count' => $pdfPageNum,
        ];
    }

    /**
     * 获取一个案卷的卷内序号和页码对应的数组
     *
     * @param $list
     * @return array
     * @author: lxk
     * @date: 2024-10-30 16:50
     */
    public static function getSerialNoPageNum($list) : array
    {
        $catalogSerialNos = [];
        $serialNoPageNum  = [];
        foreach ($list as $val) {
            $catalogSerialNos[$val['serial_no']] = $val['page_num'];
        }
        foreach ($catalogSerialNos as $key=>$pageNum) {
            if (is_numeric($pageNum)) {
                if (isset($catalogSerialNos[$key + 1])) {
                    $nextPageNum = $catalogSerialNos[$key + 1];
                    if (is_numeric($nextPageNum)) {
                        $serialNoPageNum[$key] = $nextPageNum - $pageNum;
                    } else {
                        $pageNumInfo = BookCatalogService::getSmallAndBigPage($nextPageNum);
                        $serialNoPageNum[$key] = $pageNumInfo['small_page'] - $pageNum;
                    }
                }
            } else {
                $pageNum = BookCatalogService::getSmallAndBigPage($pageNum);
                $serialNoPageNum[$key] = ($pageNum['big_page'] - $pageNum['small_page']) + 1;
            }
        }

        return $serialNoPageNum;
    }

    /**
     * @param $type
     * @param $isEffectCatalog
     * @return string
     * @author: lxk
     * @date: 2024/1/13 16:31
     */
    public function getType($type, $isEffectCatalog) : string
    {
        if ($type == 1) {
            $typeText = '档案号';
        } elseif ($type == 2) {
            $typeText = '案卷题名';
            if ($isEffectCatalog) {
                $typeText = '案卷题名和卷内文件材料题名';
            }
        } elseif ($type == 3) {
            $typeText = '卷内责任者';
        } else {
            $typeText = '卷内文件材料题名';
        }

        return $typeText;
    }

    /**
     * 替换案卷数据
     * $type: 1档案号，2案卷题名
     */
    public function replaceTitleStr($bookTitleInstance, $bookTitleIds, $type, $beforeStr, $afterStr)
    {
        if ($type == 1) {
            $bookTitleInstance
                ->when($bookTitleIds, function ($query) use($bookTitleIds) {
                    $query->whereIn('id', $bookTitleIds);
                })
                ->select('id', 'archives_no');
        } elseif ($type == 2) {
            $bookTitleInstance
                ->when($bookTitleIds, function ($query) use($bookTitleIds) {
                    $query->whereIn('id', $bookTitleIds);
                })
                ->where('title', 'like', "%{$beforeStr}%")
                ->select('id', 'title');
        }
        $bookTitleInstance->chunk(1000, function ($bookTitles) use ($type, $beforeStr, $afterStr) {
            foreach ($bookTitles as $bookTitle) {
                if ($type == 1) {
                    $title = str_replace($beforeStr, $afterStr, $bookTitle->archives_no);
                    $bookTitle->where('id', $bookTitle['id'])->update(['archives_no' => format_archives_no($title)]);
                } else {
                    $title = str_replace($beforeStr, $afterStr, $bookTitle->title);
                    $bookTitle->where('id', $bookTitle['id'])->update(['title' => $title]);
                }
            }
        });
    }

    /**
     * 替换案卷数据
     * $type: 3责任者，4材料题名
     */
    public function replaceCatalogStr($bookCatalogInstance, $bookTitleIds, $type, $beforeStr, $afterStr)
    {
        if ($type == 3) { //责任者
            $bookCatalogInstance
                ->when($bookTitleIds, function ($query) use($bookTitleIds) {
                    $query->whereIn('book_title_id', $bookTitleIds);
                })
                ->where('director', 'like', "%{$beforeStr}%")
                ->select('id', 'director');
        } elseif ($type == 4) { //材料题名
            $bookCatalogInstance
                ->when($bookTitleIds, function ($query) use($bookTitleIds) {
                    $query->whereIn('book_title_id', $bookTitleIds);
                })
                ->where('title', 'like', "%{$beforeStr}%")
                ->select('id', 'title');
        }
        $bookCatalogInstance->chunk(1000, function ($bookCatalogs) use ($type, $beforeStr, $afterStr) {
                foreach ($bookCatalogs as $bookCatalog) {
                    if ($type == 3) {
                        $director = str_replace($beforeStr, $afterStr, $bookCatalog->director);
                        $bookCatalog->update(['director' => $director]);
                    } elseif($type == 4) {
                        $title = str_replace($beforeStr, $afterStr, $bookCatalog->title);
                        $bookCatalog->update(['title' => $title]);
                    }
                }
            });
    }

    /**
     * 案卷详情
     * $type //点击的类型：1第一卷, 2上一卷, 3下一卷, 4最后卷
     * $currentId //当前案卷目录id
     */
    public function detail($type, $currentId, $partCompanyId, $projectClassifyId)
    {
        $bookTitleInstance = $this->getBookTitleInstance($partCompanyId, $projectClassifyId);
        $info = $bookTitleInstance->where('id', '=', $currentId)->first(); //当前案卷
        $archivesNoInfo = self::getSerialNos($bookTitleInstance, $info->archives_no);
        $main           = $archivesNoInfo['main'];
        $minSerialNo    = $archivesNoInfo['min'];
        $maxSerialNo    = $archivesNoInfo['max'];
        switch ($type) {
            case 1: //第一卷
                $info = $bookTitleInstance->oldest('serial_no')->first();
                break;
            case 2: //上一卷
                $preSerialNo = $bookTitleInstance->where('serial_no', '<', $info->serial_no)->max('serial_no'); //上一条数据 id
                $info        = $bookTitleInstance->where('serial_no', $preSerialNo)->first();
                break;
            case 3: //下一卷
                $nextSerialNo = $bookTitleInstance->where('serial_no', '>', $info->serial_no)->min('serial_no'); //下一条数据 id
                $info         = $bookTitleInstance->where('serial_no', $nextSerialNo)->first();
                break;
            case 4: //最后卷
                $info = $bookTitleInstance->latest('serial_no')->first();
                break;
        }
        if (empty($info)) { //如果数据不存在，返回当前数据
            $info = $bookTitleInstance->oldest('id')->first();
        }
        $wordMaterial = $info->page_num - $info->blueprint;
        $info->main_archive   = $main;
        $info->first_archive  = $main . '-' . $minSerialNo;
        $info->end_archive    = $main . '-' . $maxSerialNo;
        $info->append_remark  = "本案卷内共 {$info->page_num} 页，其中文字材料 {$wordMaterial} 页，图样 {$info->blueprint} 张，照片 0 张。";
        $info->start_end_date = format_date($info->start_date, $info->end_date);
        $info->now_location   = CommonService::getNowLocation($partCompanyId, $projectClassifyId) . ' 档案号：' . $info->archives_no;

        return $info->toArray();
    }

    /**
     * @param $bookTitleInstance
     * @param $archivesNo
     * @return array
     * @author: lxk
     */
    public static function getSerialNos($bookTitleInstance, $archivesNo): array
    {
        $archivesNoInfo = CommonService::getArchivesPart($archivesNo);
        $main           = $archivesNoInfo['main'];
        if ($res = redis()->get($main)) {
            return json_decode($res, true);
        }
        $archivesNos = $bookTitleInstance->where('archives_no', 'like', "{$main}%")->pluck('archives_no');
        $serialNoArr = [];
        foreach ($archivesNos as $archivesNo) {
            $archivesNoInfo = CommonService::getArchivesPart($archivesNo);
            $serialNoArr[]  = $archivesNoInfo['ext'];
        }
        $res = [
            'main' => $main,
            'min'  => min($serialNoArr),
            'max'  => max($serialNoArr)
        ];
        redis()->setex($main, 120, json_encode($res));

        return $res;
    }

    /**
     * 检查数据合法性
     * 如果某个案卷检查完毕，那就把这个案卷的起止日期和页次更新进来
     * @author: lxk
     * @date: 2023/5/22 16:54
     */
    public function checkData($partCompanyId, $projectClassifyId, $selectIds)
    {
        $bookTitleInstance   = $this->getBookTitleInstance($partCompanyId, $projectClassifyId);
        $bookCatalogInstance = $this->getBookCatalogInstance($partCompanyId, $projectClassifyId);

        $info = $bookTitleInstance->first();
        if (empty($info)) throw new ApiException('暂无案卷！');
        $combinationId = (new ProjectService())->getDossierCombination($partCompanyId, $projectClassifyId);
        $redisKey      = get_rand_str('book_title'); //使用集合存放标题
        $selectIds     = to_array($selectIds); //如果选择了案卷
        $checkCountKey = get_rand_str('check_count'); //检查了几条案卷
        redis()->set($checkCountKey, 0);

        $bookTitleInstance //第一步，先检查案卷数据。（1）如果指定案卷，只检查指定案卷
            ->where('is_need_check', 1) //检查结束后不需要检查的会标记为0
            ->when($selectIds, function ($query) use ($selectIds) {
                $query->whereIn('id', $selectIds);
            })
            ->select('id', 'serial_no', 'archives_no', 'title', 'page_num', 'blueprint', 'word_material', 'start_date', 'end_date', 'problem')
            ->orderBy('id') //注意，既然chunkById()了，这里必须用id排序，否则如果id从小到大之后但是serial_no不是从小到大，就会进入死循环报错
            ->chunkById(100, function ($list) use ($bookTitleInstance, $bookCatalogInstance, $combinationId, $redisKey, $checkCountKey) {
                foreach ($list as $val) { //页次的值不等于蓝图和文字材料相加..逻辑待定
                    if ($val['title'] != '等待合并') { //等待合并的案卷不检查
                        $id = to_array($val['id']);
                        $this->bookCatalogService->checkBookCatalogData($bookTitleInstance, $bookCatalogInstance, $val['id'], $combinationId);
                        if (!redis()->sadd($redisKey, $val['title'])) { //如果加入失败，说明标题重复
                            self::problemMark($bookTitleInstance, $id, '案卷题名重复!请复制名称搜索');
                        }
                        $bookTitleInstance->where('id', $val['id'])->update(['is_need_check' => 0]);
                        redis()->incrby($checkCountKey, 1);
                    }
                }
            });

        $checkCount = redis()->get($checkCountKey);
        redis()->del($redisKey); //redis临时变量用完清除
        redis()->del($checkCountKey);

        $errorCount = $bookTitleInstance->where('problem', '!=', '')->count('id');
        $emptyCount = $bookTitleInstance->where('is_have_data', 0)->count('id');
        if (!empty($selectIds)) {
            $selectCount = count($selectIds);
            $msg = "检查了勾选的【{$selectCount}】个案卷";
        } else {
            $msg = $checkCount ? "本次检查了【{$checkCount}】个案卷" : "本次没有案卷需要检查";
        }
        $msg = $msg . "；当前共有【{$emptyCount}个空案卷】";

        return ($errorCount > 0) ? $msg . ",【{$errorCount}个异常案卷】" : $msg . "，【0个异常案卷】";
    }

    /**
     * 获取一条数据基本信息
     */
    public static function getBookTitleInfo($partCompanyId, $projectClassifyId, $id)
    {
        $bookTitleInstance = CommonService::getBookTitleInstance($partCompanyId, $projectClassifyId);

        return $bookTitleInstance->find($id);
    }

    /**
     * 案卷问题标记
     */
    public static function problemMark($bookTitleInstance, $ids, $problem)
    {
        return $bookTitleInstance->whereIn('id', $ids)->update(['problem' => $problem, 'is_need_check' => 1]);
    }

    /**
     * 分析PDF文件名，并写入数据
     *
     * @param $partCompanyId
     * @param $projectClassifyId
     * @param $uploadResult
     * @return bool
     * @throws ApiException
     * @author: lxk
     * @date: 2024-09-13 16:56
     */
    public function addNewPdf($partCompanyId, $projectClassifyId, $uploadResult)
    {
        $originName  = $uploadResult['origin_name'];
        $pos         = strrpos($originName, '-');     //最后'-'出现的位置
        $archivesNo  = substr($originName, 0, $pos);  //档案号
        $serialNo    = substr($originName, $pos + 1); //序号

        $bookTitleInstance   = CommonService::getBookTitleInstance($partCompanyId, $projectClassifyId);
        $bookCatalogInstance = $this->getBookCatalogInstance($partCompanyId, $projectClassifyId);
        $bookTitle           = $bookTitleInstance->where('archives_no', $archivesNo)->select('id', 'project_classify_id')->first();
        if (empty($bookTitle)) { //如果按照档案号找不到，把档案号主部分的'-'使用 '/' 替换，看是否能找到
            if (str_contains($archivesNo, '--')) $archivesNo = str_replace('--', '/', $archivesNo);
            $bookTitle = $bookTitleInstance->where('archives_no', $archivesNo)->select('id', 'project_classify_id')->first();
            if (empty($bookTitle)) throw new ApiException("找不到案卷{$archivesNo}，档案号如果存在斜杠，斜杠使用双横杠--代替");
        }
        $bookCatalog = $bookCatalogInstance
            ->where('book_title_id', $bookTitle->id)
            ->where('serial_no', $serialNo)
            ->first();
        if (empty($bookCatalog)) throw new ApiException("找不到卷内序号 {$serialNo}");
        $bookCatalog->file_url = 1; //标记为有pdf
        $bookCatalog->save();

        //增加一条数据
        $bookCatalogPdfInstance                  = $this->getBookCatalogPdfInstance($partCompanyId, $projectClassifyId);
        $bookCatalogPdfInstance->book_title_id   = $bookTitle->id;
        $bookCatalogPdfInstance->book_catalog_id = $bookCatalog->id;
        $bookCatalogPdfInstance->file_url        = $uploadResult['file'];
        $bookCatalogPdfInstance->file_size       = $uploadResult['file_size'];
        $bookCatalogPdfInstance->origin_name     = $originName;
        return $bookCatalogPdfInstance->save();
    }

    /**
     * 回收站数据
     */
    public function getRecycles($request): array
    {
        $thisQuery = BookTitle::onlyTrashed();

        if ($request->filled('keyword')) { //判断参数是否存在并且不为空
            $keyword = $request->input('keyword');
            $thisQuery->where('title', 'like', "%{$keyword}%")->orWhere('id', '=', $keyword); //由于不同的表字段不同，所以单独拿出来
        }

        return $this->formatRecycleData($request, $thisQuery);
    }

    /**
     * 删除若干数据后，剩余的数据序号和档案号重新排列，第一个序号为1 从小到大递增
     *
     * @param $partCompanyId
     * @param $projectClassifyId
     * @param $bookTitleIds
     * @return bool
     * @throws ApiException
     * @author: lxk
     * @date: 2024-09-13 15:38
     */
    public function delData($partCompanyId, $projectClassifyId, $bookTitleIds) : bool
    {
        $bookTitleInstance   = $this->getBookTitleInstance($partCompanyId, $projectClassifyId);
        $bookCatalogInstance = $this->getBookCatalogInstance($partCompanyId, $projectClassifyId);
        if (!$bookTitleInstance->whereIn('id', $bookTitleIds)->delete()) {
            throw new ApiException('删除失败！');
        };
        /**
         * 下面如果案卷下面有卷内，删除卷内, 如果卷内有PDF,把PDF从OSS删除
         */
        $bookCatalogIds = $bookCatalogInstance->whereIn('book_title_id', $bookTitleIds)->pluck('id');
        if (!empty($bookCatalogIds)) {
            $bookCatalogInstance->whereIn('book_title_id', $bookTitleIds)->delete(); //删除对应案卷的卷内
            $this->bookCatalogService->delCatalogPdf($partCompanyId, $projectClassifyId, $bookCatalogIds); //删除卷内PDF
        }
        $ids = implode(',', $bookTitleIds);
        CommonService::addProjectActionLog("删除案卷", $ids, $partCompanyId, $projectClassifyId);

        return true;
    }

    /**
     * 判断当前用户是否可以操作案卷和卷内
     * 如果用户所在项目的参见单位和当前参见单位不一致，return 0;
     */
    public static function isAllowOperate($partCompanyId)
    {
        $projectId = PartCompany::query()->where('id', $partCompanyId)->value('project_id');

        $result = ProjectService::partCompanyUser($projectId);

        if ((!$result || !in_array($partCompanyId, $result['ids'])) && !CommonService::adminLevel() && !is_super_admin()) {
            return false;
        }

        return true;
    }

    /**
     * 把档案号存到 redis集合里面
     */
    public static function saveArchivesNoToRedis($bookTitleInstance, $projectClassifyId)
    {
        $archivesRedisKey = self::getArchivesNoRedisKey($projectClassifyId);

        $archivesNoArr = $bookTitleInstance
            ->pluck('archives_no')
            ->toArray();
        foreach ($archivesNoArr as $archivesNo) {
            redis()->sadd($archivesRedisKey, $archivesNo);
        }
        redis()->expire($archivesRedisKey, 7200);
    }

    /**
     * 获取案卷目录存放档案号的集合 redis-key
     * $value 可以是
     */
    public static function getArchivesNoRedisKey($projectClassifyId)
    {
        $userId  = user_id();
        $loginIp = get_ip_num();
        $date    = date('Ymd');

        return "archives_no:{$projectClassifyId}:{$userId}:{$date}:{$loginIp}";
    }

    /**
     * 清除 redis 集合数据
     */
    public static function clearArchivesNo($projectClassifyId)
    {
        $archivesRedisKey = self::getArchivesNoRedisKey($projectClassifyId);

        redis()->del($archivesRedisKey);
    }

    /**
     * 档案号后缀需要补充几个前导 0，例如  有的档案号 1，有的 001， 有的 0001
     * 如果原来：J•SJKZ•5ZH123/169-1 ，$zeroCount为2，会变为 J•SJKZ•5ZH123/169-001
     */
    public function zeroFill($partCompanyId, $projectClassifyId, $zeroCount)
    {
        $bookTitleInstance = CommonService::getBookTitleSearchInstance($partCompanyId, $projectClassifyId);
        $bookTitleInstance
            ->select('id', 'serial_no', 'archives_no')
            ->orderBy('serial_no')
            ->chunk(100, function ($list) use ($bookTitleInstance, $zeroCount) {
                foreach ($list as $val) {
                    $info   = CommonService::getArchivesPart($val['archives_no']);
                    $main   = $info['main']; //档案号主要部分
                    $ext    = $info['ext']; //档案号后缀
                    $newExt = $this->formatSerialNo($ext, $zeroCount);
                    $bookTitleInstance->where('id', $val['id'])->update(['archives_no' => $main . '-' . $newExt]);
                }
            });
    }

    /**
     * 导出项目全部案卷
     */
    public function exportByProject($projectId)
    {
        $project = Project::query()->find($projectId);
        $dirName = $project->title . '全部案卷目录';

        switch ($project->combination_id) {
            case 1:
                (new ProjectBookTitleExport29($projectId, $dirName))->store('public/excels/' . $dirName . '/' . date('H时i分s秒') . '.xlsx');
                break;
            case 2:
                (new ProjectBookTitleExport8($projectId, $dirName))->store('public/excels/' . $dirName . '/' . date('H时i分s秒') . '.xlsx');
                break;

        }
        return CommonService::createZip("storage/excels/{$dirName}");
    }

    /**
     * 清空案卷
     */
    public function clearData($partCompanyId, $projectClassifyId)
    {
        $bookCatalogIds = $this->getBookCatalogInstance($partCompanyId, $projectClassifyId)->pluck('id');
        if ($bookCatalogIds->isNotEmpty()) {
            $bookCatalogIds = $bookCatalogIds->toArray();
            $this->bookCatalogService->delCatalogPdf($partCompanyId, $projectClassifyId, $bookCatalogIds); //删除卷内PDF
        }
        $bookTitle = "book_titles_{$partCompanyId}_{$projectClassifyId}";
        if (Schema::hasTable($bookTitle)) {
            DB::table($bookTitle)->truncate();
        }
        $bookCatalog = "book_catalogs_{$partCompanyId}_{$projectClassifyId}";
        if (Schema::hasTable($bookCatalog)) {
            DB::table($bookCatalog)->truncate();
        }
        $bookCatalogPdf = "book_catalog_pdfs_{$partCompanyId}_{$projectClassifyId}";
        if (Schema::hasTable($bookCatalogPdf)) {
            DB::table($bookCatalogPdf)->truncate();
        }
    }

}
